﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Text;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using Library.WebControls.Ds;
using Library.WebControls.SimplePaging;
using Image = System.Web.UI.WebControls.Image;
using System.IO;
using System.Linq;

namespace Library.WebControls.SimpleGridView {
	///<summary>
	///</summary>
	[ToolboxBitmap(typeof(SpGridView), "Library.WebControls.SimpleDateEdit.SpGridView.bmp")]
	[Designer(typeof(SpGridViewDesign))]
	[SupportsEventValidation]
	[DesignTimeVisible(true)]
	[DefaultProperty("Columns")]
	[ToolboxData("<{0}:SpGridView runat=server></{0}:SpGridView>")]
	public class SpGridView : BaseAjaxControl, IPagingControl {

		private const string SpGridViewControlCallbackParameter = "__SPGRIDVIEWRQP_";
		private const string SpGridViewControlCallbackFunction = "__SPGRIDVIEWRQF_";
		private const string SpGridViewControlCurrentPageIndex = "__SPGRIDVIEWCPID_";
		private const string SpGridViewControlCurrentPageSize = "__SPGRIDVIEWCPS_";
		private const string SpGridViewControlSortColumnIndex = "__SPGRIDVIEWSCI_";
		private const string SpGridViewControlSortType = "__SPGRIDVIEWST_";
		private const string SpGridViewControlParrentKey = "__SPGRIDVIEWPK_";
		private const string SpGridViewControlExpendedKey = "__SPGRIDVIEWEXK_";
		private const string SpGridViewControlServerId = "__SPGRIDVIEWSID_";
		private const string SpGridViewControlSaveEditParameter = "__SPGRIDVIEWSEPR_";
		private const string SpGridViewControlPrimaryKey = "__SPGRIDVIEWPRK_";
		private const string SpGridViewControlFilterData = "__SPGRIDVIEWFD_";
		private static readonly object PageIndexChangedEvent = new object();
		private int _rowsOnBinding;
		//internal const string SplitterString = "_|-!^;~";
		///<summary>
		/// Check the page is callback by gridview?
		///</summary>
		///<param name="page"></param>
		///<returns></returns>
		public static bool IsGridViewCallback(Page page) {
			try {
				return !String.IsNullOrEmpty(page.Request[SpGridViewControlServerId]);
			} catch {
				return false;
			}
		}


		protected override string StyleSheetFileName {
			get {
				return "spgrid-ui.css";
			}
		}

		/// <summary>
		/// Get this grid has child grid
		/// </summary>
		[Browsable(false)]
		[DefaultValue(false)]
		public bool HasChildGrid {
			get { return ChildGridView != null; }
		}
		protected RequestType RequestType;
		protected string RequestParameter = String.Empty;
		protected int SortColumnIndex = -1;
		/// <summary>
		/// Get sort column index send from client on callback
		/// </summary>
		[Browsable(false)]

		public int RequestSortColumnIndex {
			get { return SortColumnIndex; }
		}
		/// <summary>
		/// /Get Sort type send from client on callback
		/// </summary>
		[Browsable(false)]
		public SortType RequestSortType {
			get { return SortType; }
		}
		protected SortType SortType = SortType.None;

		/// <summary>
		/// Get filter data collection send from client on callback
		/// </summary>
		[Browsable(false)]
		public FilterDataCollection FilterDatas { get; private set; }
		/// <summary>
		/// Get parent key (on Child grid mode) from client request on callback
		/// </summary>
		[Browsable(false)]
		[DefaultValue("")]
		public string ParrentKey { private set; get; }
		/// <summary>
		/// Get or set grid show loading panel on callback
		/// </summary>
		[DefaultValue(false)]
		public bool ShowLoadingPanel { get; set; }
		protected string ExpendedKey = String.Empty;
		[DefaultValue(1)]
		public int PageIndex {
			get {
				EnsureChildControls();
				return _divGridView.PagingControl.PageIndex;
			}

			set {
				EnsureChildControls();
				_divGridView.PagingControl.PageIndex = value;
			}
		}
		[DefaultValue(10)]
		public int PageSize {
			get {
				EnsureChildControls();
				return _divGridView.PagingControl.PageSize;
			}

			set {
				EnsureChildControls();
				_divGridView.PagingControl.PageSize = value;
			}
		}
		[Browsable(false)]
		[DefaultValue(0)]
		public int TotalItems {
			get {
				EnsureChildControls();
				return _divGridView.PagingControl.TotalItems;
			}

			set {
				EnsureChildControls();
				_divGridView.PagingControl.TotalItems = value;
			}
		}

		/// <summary>
		/// Create gridview instance
		/// </summary>
		public SpGridView() {
			InitControl();
			ParrentKey = String.Empty;

		}

		protected void InitControl() {
			CollapseImagePath = "~/style/simple/images/collapsed.png";
			ExpendedImagePath = "~/style/simple/images/expanded.png";
			//NoItemDisplayText = "No item display";
			ClientScript = new ClientScript();
			PagingType = PagingType.Automatic;
			EditType = EditType.None;
			ExportGridStyle = new TableStyle(ViewState);
			HeaderRowStyle = new HeaderRowStyle();
			FilterDatas = new FilterDataCollection(this);

		}

		private EditType _editType;
		/// <summary>
		/// Get or set grid edit type
		/// </summary>
		public EditType EditType {
			get {
				EnsureChildControls();
				return _editType;
			}
			set {
				EnsureChildControls();
				switch (value) {
					case EditType.None:
						_divGridView.DivEditting.Visible = false;
						break;
					case EditType.Inline:
						_divGridView.DivEditting.Visible = true;
						break;
				}
				_editType = value;
			}
		}
		internal SpGridView ParrentGrid;
		/// <summary>
		/// Get or set image collapse path (on child grid mode)
		/// </summary>
		[Bindable(true)]
		[DefaultValue("")]
		[UrlProperty]
		public String CollapseImagePath { get; set; }
		/// <summary>
		/// Get or set image expended path (on child grid mode)
		/// </summary>
		[Bindable(true)]
		[DefaultValue("")]
		[UrlProperty]
		public String ExpendedImagePath { get; set; }
		[Browsable(false)]
		public object DataSource { get; set; }
		//private SpGvColumnCollection _columns;
		private SpDivGridView _divGridView;
		/// <summary>
		/// Get script on client, User can use this function to hook into grid script execute on client
		/// </summary>
		[PersistenceMode(PersistenceMode.InnerProperty)]
		[MergableProperty(false)]
		[Category("Data")]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
		[Browsable(false)]
		public ClientScript ClientScript { private set; get; }
		/// <summary>
		/// Get grid header row style
		/// </summary>
		[PersistenceMode(PersistenceMode.InnerProperty)]
		[MergableProperty(false)]
		[Category("Data")]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]

		public HeaderRowStyle HeaderRowStyle { private set; get; }

		/// <summary>
		/// Get or set child grid
		/// </summary>

		
		
		
		
		[DefaultValue(false)]
		public bool PreventUnSelectByClick { get; set; }
		public SpGridView ChildGridView {
			get {
				EnsureChildControls();
				return _divGridView.ChildGrid;
			}
			set {
				EnsureChildControls();
				_divGridView.ChildGrid = value;
			}
		}

		//[Description("Provides access to an SimpleGridView's edit template form.")]
		/// <summary>
		/// Get paging control of this grid
		/// </summary>
		[PersistenceMode(PersistenceMode.InnerProperty)]
		[MergableProperty(false)]
		[Category("Data")]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
		[DefaultValue("")]
		public SpPaging PagingControl {
			get {
				EnsureChildControls();
				return _divGridView.PagingControl;
			}
		}
		/// <summary>
		/// Get or set client grid visible
		/// </summary>
		public override bool ClientVisible {
			get {
				EnsureChildControls();
				return _divGridView.ClientVisible;
			}
			set {
				EnsureChildControls();
				_divGridView.ClientVisible = value;
			}
		}

		protected override void CreateChildControls() {
			_headerDescImage = new Image { ImageUrl = "~/style/simple/images/sort_desc.png" };
			_headerAscImage = new Image { ImageUrl = "~/style/simple/images/sort_inc.png" };
			_divGridView = new SpDivGridView { GridView = this };
			Controls.Add(_divGridView);
			Columns.ParrentStyle = _divGridView.TableGrid.ControlStyle;
			//_columns = new SpGvColumnCollection { ParrentStyle = _divGridView.TableGrid.ControlStyle };
		}
		/// <summary>
		/// Get or set paging type
		/// </summary>
		public PagingType PagingType { get; set; }
		/// <summary>
		/// Get or set the field name of grid key. Used in inline edit mode, and delete row event
		/// </summary>
		public string KeyFieldName { get; set; }
		[DefaultValue(false)]
		public bool EnableSort { get; set; }
		[DefaultValue(false)]
		public bool EnableFilter { get; set; }

		private Image _headerDescImage;
		public Image HeaderDescImage {
			get {
				EnsureChildControls();
				return _headerDescImage;
			}
		}

		private Image _headerAscImage;
		public Image HeaderAscImage {
			get {
				EnsureChildControls();
				return _headerAscImage;
			}
		}

		public string ConfirmDeleteMessage { get; set; }

		protected virtual void CreateFooterRow() {
			var handler = (EventHandler<SpGvFooterRowCreating>)Events[FooterRowCreatingEvent];
			if (handler != null) {
				var footerRow = new TableRow { CssClass = "spgvFooterRow" };
				_divGridView.TableGrid.Rows.Add(footerRow);
				for (int i = 0; i < VisibleColumns.Count; i++) {

					var footerCell = new TableCell { CssClass = i > 0 ? "spgvCell" : "spgvFirstCell" };
					footerRow.Cells.Add(footerCell);
				}

				if (HasChildGrid) {
					footerRow.Cells[0].CssClass = "spgvCell";
					var firsCell = new TableCell { CssClass = "spgvFirstCell" };
					footerRow.Cells.AddAt(0, firsCell);
				}


				handler(this, new SpGvFooterRowCreating { FooterRow = footerRow });
			}
		}
		protected virtual void CreateHeaderRow() {
			var headerRow = new TableRow {
				CssClass = EnableSort ? "spgvSortHeaderRow" : "spgvHeaderRow"
			};
			HeaderRowStyle.HeaderStyle.CopyStyleToRow(headerRow);
			_divGridView.TableGrid.Rows.AddAt(0, headerRow);
			for (int i = 0; i < VisibleColumns.Count; i++) {
				var column = VisibleColumns[i];
				var headerCell = new TableCell { CssClass = i > 0 ? "spgvCell" : "spgvFirstCell" };

				if (column.HeaderTemplate != null) {
					column.HeaderTemplate.InstantiateIn(headerCell);
				} else {
					headerCell.Controls.Add(new LiteralControl(column.Caption));
				}


				if (EnableSort && column.EnableSort) {
					var sortType = (Int32)SortType.Asc;
					if (i == SortColumnIndex) {
						if (SortType == SortType.Des) {
							headerCell.Controls.Add(HeaderDescImage);
						} else if (SortType == SortType.Asc) {
							sortType = (Int32)SortType.Des;
							headerCell.Controls.Add(HeaderAscImage);
						}
					}

					headerCell.Attributes["onclick"] = String.Format("{0}.Sort({1}, {2});", ClientName, i, sortType);
				}

				column.HeaderStyle.CopyStyleToCell(headerCell);
				headerRow.Cells.Add(headerCell);
			}

			if (HasChildGrid) {
				headerRow.Cells[0].CssClass = "spgvCell";
				var firsCell = new TableCell { CssClass = "spgvFirstCell" };
				headerRow.Cells.AddAt(0, firsCell);
			}
			if (EnableFilter) {
				var filterRow = new TableRow();
				filterRow.CssClass = "spfilterrow";
				bool hadFirst = false;
				if (HasChildGrid) {
					var firsCell = new TableCell { CssClass = "spgvFirstCell" };
					filterRow.Cells.Add(firsCell);
					hadFirst = true;
				}
				for (var columnIndex = 0; columnIndex < VisibleColumns.Count; columnIndex++) {
					var visibleColumn = VisibleColumns[columnIndex];
					var filterCell = new TableCell();
					if (hadFirst) filterCell.CssClass = "spgvFirstCell";
					else {
						hadFirst = true;
						filterCell.CssClass = "spgvFirstCell";
					}
					if (visibleColumn.Filter != FilterType.None) {
						var div = new HtmlGenericControl("div");
						div.Attributes["onkeypress"] = String.Format("return {0}.Call(2,event);", ClientName);
						div.Attributes["class"] = "filterdiv";
						var textBox = new TextBox();
						div.Controls.Add(textBox);
						filterCell.Controls.Add(div);
						var index = columnIndex;
						var f = FilterDatas.FirstOrDefault(fd => fd.ColumnIndex == index);
						if (f != null) {
							textBox.Text = f.Value;
						}
					}

					filterRow.Cells.Add(filterCell);
				}
				_divGridView.TableGrid.Rows.AddAt(1, filterRow);
			}
			int empltyRowCount = 1;
			if (EnableFilter) empltyRowCount = 2;
			if (_divGridView.TableGrid.Rows.Count == empltyRowCount) {
				var empltyRow = new TableRow { CssClass = "spgvEmptyRow" };
				_divGridView.TableGrid.Rows.Add(empltyRow);

				var emptyCell = new TableCell { ColumnSpan = headerRow.Cells.Count, Text = NoItemDisplayText, CssClass = "spgvFirstCell", HorizontalAlign = HorizontalAlign.Center, VerticalAlign = VerticalAlign.Middle };
				empltyRow.Cells.Add(emptyCell);
			}
		}
		public override void DataBind() {
			_rowsOnBinding = 0;
			EnsureChildControls();
			var before = (EventHandler)Events[BeforeDataBindingEvent];
			if (before != null) {
				before(this, EventArgs.Empty);
			}
			_divGridView.TableGrid.Rows.Clear();
			if (VisibleColumns.Count == 0) return;
			if (DataSource != null) {
				var sourceWrapper = new DataSourceWrapper(DataSource);
				if (PagingType == PagingType.Manual) {
					int baseDataIndex = PageSize * (PageIndex - 1);
					while (sourceWrapper.MoveNext()) {
						AddRow(sourceWrapper.Current, baseDataIndex + sourceWrapper.Index, sourceWrapper.Index);
					}
				} else {
					var processingSource = new Enumerable<DataSourceWrapper.DataItemWraper>();
					var sortSource = new List<DataSourceWrapper.DataItemWraper>();
					TotalItems = 0;
					if (SortColumnIndex < 0 || SortColumnIndex >= VisibleColumns.Count || SortType == SortType.None) {
						while (sourceWrapper.MoveNext()) {
							TotalItems++;
							processingSource.AddItem(sourceWrapper.Current);
						}
					} else {
						while (sourceWrapper.MoveNext()) {
							TotalItems++;

							sortSource.Add(sourceWrapper.Current);
						}
						var sortColumn = VisibleColumns[SortColumnIndex];

						DataSourceWrapper.DataItemWraper[] source = sortSource.ToArray();
						Array.Sort(source, new CompateDataItemWrapper(sortColumn));
						if (SortType == SortType.Asc) {
							foreach (DataSourceWrapper.DataItemWraper t in source) {
								//bool canAdd = MatchFilter(source[i]);
								//if (canAdd) {
								processingSource.AddItem(t);

								//}
							}
						} else if (SortType == SortType.Des) {
							for (int i = source.Length - 1; i >= 0; i--) {
								//bool canAdd = MatchFilter(source[i]);
								//if (canAdd) {
								processingSource.AddItem(source[i]);
								//}
							}
						}
					}

					IEnumerator<DataSourceWrapper.DataItemWraper> rator = processingSource.GetEnumerator();
					int offSet = (PageIndex - 1) * PageSize;
					int dataItemIndex = offSet;
					int displayIndex = 0;
					int counter = PageSize;
					while (rator.MoveNext()) {
						if (offSet > 0) {
							offSet--;
							continue;
						}

						if (counter > 0) {
							AddRow(rator.Current, dataItemIndex, displayIndex);
							counter--;
							displayIndex++;
							dataItemIndex++;
						} else {
							break;
						}
					}
				}
			} else {
				TotalItems = 0;
			}

			if (_rowsOnBinding == 0) {

				var cell = new TableCell { Text = NoItemDisplayText, ColumnSpan = VisibleColumns.Count, HorizontalAlign = HorizontalAlign.Center };
				cell.Style.Add(HtmlTextWriterStyle.PaddingTop, "5px");
				cell.Style.Add(HtmlTextWriterStyle.PaddingBottom, "5px");
				var row = new TableRow();
				row.Cells.Add(cell);
				_divGridView.TableGrid.Rows.Add(row);
			}
			base.DataBind();
			//_divGridView.BindingEditControl();
		}

		private bool MatchFilter(DataSourceWrapper.DataItemWraper item) {
			if (!EnableFilter) return true;
			if (FilterDatas.Count == 0) return true;
			for (int i = 0; i < VisibleColumns.Count; i++) {
				var column = VisibleColumns[i];
				switch (column.Filter) {
					case FilterType.Enter:
						if (String.IsNullOrEmpty(column.FieldName)) {
							throw new Exception("Field name not found in column: " + column.Caption + ". In automatic paging mode, Field name must have value on filter column");
						}
						var filterValue = FilterDatas.FindValue(i);
						if (!String.IsNullOrEmpty(filterValue)) {
							string[] arrayFilter;
							var isArrayFilter = ParseFilterArr(filterValue, out arrayFilter);

							var v = item.GetPropertyValue(column.FieldName);
							if (v == null) return false;
							switch (column.FieldType) {
								case DataFieldType.Text:
									var strValue = v.ToString();
									if (isArrayFilter) {
										if (String.Compare(arrayFilter[0], strValue, true) > 0 || String.Compare(strValue, arrayFilter[1], true) > 0)
											return false;
									} else {

									}
									break;
								case DataFieldType.Numeric:
									break;
								case DataFieldType.Date:
									break;
								default:
									return false;
							}
						}
						break;
				}
			}

			return true;
		}

		private static bool ParseFilterArr(string filterData, out string[] array) {

			array = filterData.Split(new[] { ".." }, StringSplitOptions.None);
			return array.Length == 2;

		}

		public string NoItemDisplayText { get; set; }

		public Unit Width {
			get {
				EnsureChildControls();
				return new Unit(_divGridView.Style[HtmlTextWriterStyle.Width]);
			}
			set {
				EnsureChildControls();
				_divGridView.Style[HtmlTextWriterStyle.Width] = value.ToString();
			}
		}

		internal string ClientScriptHandlerRowClick;
		private void AddRow(DataSourceWrapper.DataItemWraper itemWrapper, int dataItemIndex, int displayIndex) {
			_rowsOnBinding++;

			if (VisibleColumns.Count == 0) {
				return;
			}


			var contentRow = new SpGvRowItemTemplate {
				DataItem = itemWrapper.OriginalItem,
				DisplayIndex = displayIndex,
				DataItemIndex = dataItemIndex,
				CssClass = "spgvRow",

			};
			if (String.IsNullOrEmpty(ClientScriptHandlerRowClick))
				contentRow.Attributes["onclick"] = ClientName + ".HandleRowClick(this);";
			else contentRow.Attributes["onclick"] = ClientScriptHandlerRowClick;

			contentRow.Attributes["ondblclick"] = ClientName + ".Call(7,this);";
			_divGridView.TableGrid.Rows.Add(contentRow);

			for (int i = 0; i < VisibleColumns.Count; i++) {
				var column = VisibleColumns[i];
				var cell = new SpGvCellItemTemplate();

				if (i > 0 || HasChildGrid)
					cell.CssClass = "spgvCell";
				else {
					cell.CssClass = "spgvFirstCell";
				}

				column.ContentStyle.CopyStyleToCell(cell);
				contentRow.Cells.Add(cell);
				cell.DataItem = itemWrapper.OriginalItem;
				cell.DataItemIndex = dataItemIndex;
				cell.DisplayIndex = displayIndex;
				if (column.ItemTemplate == null) {
					if (!String.IsNullOrEmpty(column.FieldName)) {
						var fieldValue = itemWrapper.GetPropertyValue(column.FieldName);
						column.InsertValueToTableCell(this, cell, fieldValue, itemWrapper.OriginalItem);
					}

				} else {
					column.ItemTemplate.InstantiateIn(cell);
				}

				var cellCreatedEventArgs = new SpGvCellCreatedEventArgs(itemWrapper, cell) { Column = column };
				OnCellCreated(cellCreatedEventArgs);
			}
			OnRowCreated(new SpGvRowCreatedEventArgs(itemWrapper, contentRow));
			if (String.IsNullOrEmpty(contentRow.Key)) {
				if (!String.IsNullOrEmpty(KeyFieldName)) {
					contentRow.Key = itemWrapper.GetPropertyValue(KeyFieldName).NullToEmpty();
				}
			}

			if (HasChildGrid) {
				var firstCell = new TableCell {
					CssClass = "spgvFirstCell",
					HorizontalAlign = HorizontalAlign.Center,
					VerticalAlign = VerticalAlign.Middle
				};
				var image = new Image { ImageUrl = CollapseImagePath };
				image.Attributes["onclick"] = ClientName + ".Expended(this);";
				firstCell.Controls.Add(image);
				contentRow.Cells.AddAt(0, firstCell);
			}

		}
		[Description("")]
		[PersistenceMode(PersistenceMode.InnerProperty)]
		[MergableProperty(false)]
		[Category("Data")]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
		[DefaultValue("")]
		public SpGvColumnCollection Columns {
			get {
				EnsureChildControls();
				return _divGridView.DivEditting.Column;
			}
		}
		[Description("")]
		[PersistenceMode(PersistenceMode.InnerProperty)]
		[MergableProperty(false)]
		[Category("Data")]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]

		public TableStyle ExportGridStyle { get; private set; }
		private SpGvColumnVisibleCollection _visibleColumns;
		[Description("")]

		[MergableProperty(false)]
		[Browsable(false)]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]

		public SpGvColumnVisibleCollection VisibleColumns {
			get {
				if (_visibleColumns == null) {
					_visibleColumns = new SpGvColumnVisibleCollection();
					foreach (var column in Columns) {
						if (column.Visible) _visibleColumns.Add(column);
					}
				}

				return _visibleColumns;
			}
		}

		public override string ID {
			get {
				EnsureChildControls();
				return _divGridView.ID;
			}
			set {
				EnsureChildControls();
				_divGridView.ID = value;
			}
		}

		public override string ClientID {
			get {
				EnsureChildControls();
				return _divGridView.ClientID;
			}
		}

		//private bool _isExportData;
		//public bool IsExportData { get { return _isExportData; } }

		protected bool IsGridCallback;
		public override bool IsCallback {
			get { return IsGridCallback; }
		}
		protected override void OnInit(EventArgs e) {
			base.OnInit(e);
			RequestType = RequestType.None;
			if (Request != null) {
				IsGridCallback = IsGridViewCallback(Page) && String.Compare(_divGridView.ClientSendRequestId, Request[SpGridViewControlServerId], true) == 0;
				if (IsGridCallback) {
					ParseCallbackRequest();
				}
			} else {
				IsGridCallback = false;
			}
		}

		protected virtual void ParseCallbackRequest() {
			if (Request == null) return;
			var requestType = Request[SpGridViewControlCallbackFunction].ParseInt();
			if (requestType.HasValue) {
				RequestType = (RequestType)requestType.Value;
			}
			RequestParameter = Request[SpGridViewControlCallbackParameter];
			ParrentKey = Request[SpGridViewControlParrentKey];

			ExpendedKey = Request[SpGridViewControlExpendedKey];
			var pageIndex = Request[SpGridViewControlCurrentPageIndex].ParseInt();
			if (pageIndex.HasValue) {
				if (pageIndex.Value > 0) PageIndex = pageIndex.Value;
			}

			var pageSize = Request[SpGridViewControlCurrentPageSize].ParseInt();
			if (pageSize.HasValue) {
				if (pageSize.Value > 0) PageSize = pageSize.Value;
			}

			var sortColumnIndex = Request[SpGridViewControlSortColumnIndex].ParseInt();
			if (sortColumnIndex.HasValue && sortColumnIndex.Value >= 0 && sortColumnIndex.Value < VisibleColumns.Count) {
				SortColumnIndex = sortColumnIndex.Value;
				var sortType = Request[SpGridViewControlSortType].ParseInt();
				if (sortType.HasValue) SortType = (SortType)sortType.Value;
			}

			var filter = Request[SpGridViewControlFilterData];
			var fs = FromArrayString(filter);
			int run = 0;
			while (run < fs.Length) {
				var index = run;
				var value = run + 1;
				if (value < fs.Length) {

					int cIndex;
					if (Int32.TryParse(fs[index], out cIndex)) {
						var fd = new FilterData();
						if (HasChildGrid) cIndex--;
						fd.ColumnIndex = cIndex;
						fd.Value = fs[value];
						FilterDatas.Add(fd);
					}
				}
				run += 2;
			}
		}

		protected override void OnLoad(EventArgs e) {
			base.OnLoad(e);
			IncludeScriptOnLoad(typeof(SpGridView), "GridAjax", "Library.WebControls.SimpleGridView.include.SpGridView.js",
																							UtilLib);
			if (IsCallback) {
				switch (RequestType) {
					case RequestType.CustomCallback:
						OnCustomCallback(new CustomCallbackEventArgs(RequestParameter));
						break;
					case RequestType.DataCallback:
						OnDataCallback(new CustomCallbackEventArgs(RequestParameter));
						break;
					case RequestType.Expended:
						if (ChildGridView != null) ChildGridView.ParrentKey = ExpendedKey;
						OnRowExpended(new SpRowExpendedEventArgs(RequestParameter) { ExpendedKey = ExpendedKey });
						break;
					case RequestType.PageIndexChange:
						OnPageIndexChanged(new CustomCallbackEventArgs(String.Empty));
						break;
					case RequestType.RowEdit:
						OnRowEdit(new SpGvRowEditedEventArgs(RequestParameter, Request[SpGridViewControlSaveEditParameter], Request[SpGridViewControlPrimaryKey]) { Grid = this });
						break;
					case RequestType.RowDelete:
						OnRowDelete(new SpGvRowDeletedEventArgs(RequestParameter) { Keys = FromArrayString(Request[SpGridViewControlPrimaryKey]) });
						break;
					case RequestType.GetEditValue:
						OnGetEditValues(new GetEditValuesEventArgs(Request[RequestParameter], this));
						break;
				}
			}

		}
		[DefaultValue(false)]
		public bool EnableMultySelect { get; set; }
		protected override void CreateClientObjectAtRender(HtmlTextWriter writer) {
			BeginScriptTag(writer);
			var server = 0;
			foreach (var visibleColumn in VisibleColumns) {
				if (visibleColumn.EditValueMode == EditValueMode.QueryToServer) {
					server = 1;
					break;
				}
			}
			writer.Write(String.Format("var {0} = new SpGridView();", ClientName));
			writer.Write(String.Format("{0}.Init('{0}','{1}',{2},{3},'{4}','{5}',{6},{7},'{8}','{9}',{10},{11},{12},{13});",
					ClientName,
					_divGridView.ClientID,
					ClientEnable.ToString().ToLower(),
					EnableMultySelect.ToString().ToLower(),
					PagingControl.CurrentPageContainer.ClientID,
					PagingControl.RowPerPageContainer.ClientID,
					SortColumnIndex,
					(Int32)SortType,
					ParrentKey,
					_divGridView.ClientSendRequestId, ShowLoadingPanel ? 1 : 0,
					EnableFilter.ToString().ToLower(),
					server, PreventUnSelectByClick.ToString().ToLower()));
			if (HasChildGrid) {
				writer.Write(String.Format("{0}.InitExpendImage('{1}','{2}');", ClientName, ResolveClientUrl(CollapseImagePath), ResolveClientUrl(ExpendedImagePath)));
			}

			AddScriptHandler(writer, 1, ClientScript.BeforeCallback, "AddHandler");
			AddScriptHandler(writer, 2, ClientScript.SelectedRowChanged, "AddHandler");
			AddScriptHandler(writer, 3, ClientScript.RowClicking, "AddHandler");
			AddScriptHandler(writer, 4, ClientScript.RowClicked, "AddHandler");
			AddScriptHandler(writer, 5, ClientScript.InLineNewControlInit, "AddHandler");
			AddScriptHandler(writer, 6, ClientScript.CallbackComplete, "AddHandler");
			AddScriptHandler(writer, 7, ClientScript.RowDoubleClick, "AddHandler");

			if (!String.IsNullOrEmpty(ConfirmDeleteMessage)) {
				writer.Write("{0}.ConfirmDeleteText('{1}');", ClientName, ConfirmDeleteMessage);
			}

			EndScriptTag(writer);
		}

		protected override void Render(HtmlTextWriter writer) {
			if (String.IsNullOrEmpty(NoItemDisplayText)) {
				var basePage = Page as BasePage;
				if (basePage != null) {
					NoItemDisplayText = basePage.CulText.NoItem;
					if (String.IsNullOrEmpty(ConfirmDeleteMessage))
						ConfirmDeleteMessage = basePage.CulText.ConfirmDelete;
				}
			}
			CreateHeaderRow();
			CreateFooterRow();
			PagingControl.ProtectTablePaging.Attributes["onkeypress"] = "return " + ClientName + ".Call(1,event);";
			PagingControl.Edit.ClientOnClick = ClientName + ".InlineEdit()";
			PagingControl.Delete.ClientOnClick = ClientName + ".Call(3)";
			PagingControl.Cancel.ClientOnClick = ClientName + ".CancelEdit()";
			PagingControl.Save.ClientOnClick = ClientName + ".Call(4)";
			PagingControl.New.ClientOnClick = ClientName + ".NewEdit()";
			if (IsCallback) {
				WriteCustomCallbackCompleteResult();
				switch (RequestType) {
					case RequestType.CustomCallback:
					case RequestType.PageIndexChange:
					case RequestType.RowEdit:
					case RequestType.RowDelete:
						if (Visible) {
							PagingControl.BuildClientFunctionForPaging(ClientName + ".GetPage({0});", ClientName + ".Refresh();");
							_divGridView.RenderBodyWithoutChildGrid(writer);
						}
						break;
					case RequestType.DataCallback:
					case RequestType.GetEditValue:
						break;

					case RequestType.Expended:
						if (ChildGridView != null) {
							ChildGridView.CreateHeaderRow();
							ChildGridView.PagingControl.ProtectTablePaging.Attributes["onkeypress"] = "return " + ChildGridView.ClientName + ".Call(1,event));";
							ChildGridView.RenderMeWithoutChildGrid(writer);

						}
						break;
				}
				FlushAndEndCallback();
			} else if (IsGridViewCallback(Page)) {// is child call back ??
				_divGridView.RenderChildGridWithoutMe(writer);
			} else {
				RenderMeWithoutChildGrid(writer);
			}
		}

		protected virtual void RenderMeWithoutChildGrid(HtmlTextWriter writer) {
			if (!Visible) return;
			CreateClientObjectAtRender(writer);
			PagingControl.BuildClientFunctionForPaging(ClientName + ".GetPage({0});", ClientName + ".Refresh();");
			_divGridView.RenderMeWithoutChildGrid(writer);
		}

		protected virtual void OnRowEdit(SpGvRowEditedEventArgs e) {
			var handler = (EventHandler<SpGvRowEditedEventArgs>)Events[RowEditedEvent];
			if (handler != null) {
				try {
					handler(this, e);
					ProcessCustomCallbackEventArgs(e);
				} catch (Exception ex) {
					WriteError(ex);
				}
			}
		}

		private static readonly object CustomCallbackEvent = new object();
		public event CustomCallbackEventHandler CustomCallback {
			add {
				Events.AddHandler(CustomCallbackEvent, value);
			}
			remove {
				Events.RemoveHandler(CustomCallbackEvent, value);
			}
		}

		protected virtual void OnCustomCallback(CustomCallbackEventArgs e) {
			var handler = (CustomCallbackEventHandler)Events[CustomCallbackEvent];
			if (handler != null) {
				try {
					handler(this, e);
					ProcessCustomCallbackEventArgs(e);
				} catch (Exception ex) {
					WriteError(ex);
				}
			}
		}

		private static readonly object DataCallbackEvent = new object();
		public event CustomCallbackEventHandler DataCallback {
			add {
				Events.AddHandler(DataCallbackEvent, value);
			}
			remove {
				Events.RemoveHandler(DataCallbackEvent, value);
			}
		}

		protected virtual void OnDataCallback(CustomCallbackEventArgs e) {
			var handler = (CustomCallbackEventHandler)Events[DataCallbackEvent];

			if (handler != null) {
				try {
					handler(this, e);
					ProcessCustomCallbackEventArgs(e);
				} catch (Exception ex) {
					WriteError(ex);
				}
			}
		}

		public event EventHandler PageIndexChanged {
			add {
				Events.AddHandler(PageIndexChangedEvent, value);
			}
			remove {
				Events.RemoveHandler(PageIndexChangedEvent, value);
			}
		}

		protected virtual void OnPageIndexChanged(CustomCallbackEventArgs e) {
			var handler = (EventHandler)Events[PageIndexChangedEvent];
			if (handler != null) {
				try {
					handler(this, e);
					ProcessCustomCallbackEventArgs(e);
				} catch (Exception ex) {
					WriteError(ex);
				}
			}
		}
		/// <summary>
		/// Return UTF-8 With bom array string
		/// </summary>
		/// <param name="datasource"></param>
		/// <returns></returns>
		public byte[] ExportToExcel(object datasource) {
			var holder = CreateExcelHolder(datasource);
			if (holder == null) return null;
			var stringBuilder = new StringBuilder();
			var stringWriter = new StringWriter(stringBuilder);
			var writer = new HtmlTextWriter(stringWriter);
			holder.RenderControl(writer);
			writer.Flush();
			return new UTF8Encoding(true).GetBytes(stringWriter.ToString());
			//var bContent = Encoding.UTF8.GetBytes(stringBuilder.ToString());
			//var bBom = new byte[] { 0xEF, 0xBB, 0xBF };
			//var bToWrite = new byte[bBom.Length + bContent.Length];


			////combile the BOM and the content
			//bBom.CopyTo(bToWrite, 0);
			//bContent.CopyTo(bToWrite, bBom.Length);

			//return bToWrite;
		}
		public void ExportToExcel(object dataSource, string fileName) {
			FileStream fileStream = null;
			if (File.Exists(fileName))
				fileStream = File.OpenWrite(fileName);

			else fileStream = File.Create(fileName);
			using (fileStream) {
				ExportToExcel(dataSource, fileStream);
				fileStream.Flush();
			}

		}
		public void ExportToExcel(object dataSource, Stream stream) {
			ExportToExcel(dataSource, stream, new UTF8Encoding(true));
		}
		public void ExportToExcel(object dataSource, Stream stream, Encoding encoding) {
			var streamWriter = new StreamWriter(stream, encoding);
			var htmlWriter = new HtmlTextWriter(streamWriter);
			ExportToExcel(dataSource, htmlWriter);
		}
		public void ExportToExcel(object dataSource, HtmlTextWriter htmlWriter) {
			var holder = CreateExcelHolder(dataSource);


			//htmlWriter.Write("<html xmlns:x=\"urn:schemas-microsoft-com:office:excel\">");
			//htmlWriter.Write("<head>");
			//htmlWriter.Write("<meta http-equiv=\"Content-Type\" content=\"text/html;\">");
			//htmlWriter.Write("<!--[if gte mso 9]>");
			//htmlWriter.Write("<xml>");
			//htmlWriter.Write("<x:ExcelWorkbook>");
			//htmlWriter.Write("<x:ExcelWorksheets>");
			//htmlWriter.Write("<x:ExcelWorksheet>");
			////this line names the worksheet
			//htmlWriter.Write("<x:Name>DataSheet</x:Name>");
			//htmlWriter.Write("<x:WorksheetOptions>");
			////these 2 lines are what works the magic
			//htmlWriter.Write("<x:Panes>");
			//htmlWriter.Write("</x:Panes>");
			//htmlWriter.Write("</x:WorksheetOptions>");
			//htmlWriter.Write("</x:ExcelWorksheet>");
			//htmlWriter.Write("</x:ExcelWorksheets>");
			//htmlWriter.Write("</x:ExcelWorkbook>");
			//htmlWriter.Write("</xml>");
			//htmlWriter.Write("<![endif]-->");
			//htmlWriter.Write("</head>");
			//htmlWriter.Write("<body>");
			holder.RenderControl(htmlWriter);
			//htmlWriter.Write("</body>");
			//htmlWriter.Write("</html>");
			htmlWriter.Flush();
		}

		protected virtual PlaceHolder CreateExcelHolder(object datasource) {
			var holder = new PlaceHolder();
			var table = new Table();
			holder.Controls.Add(table);
			table.ControlStyle.CopyFrom(ExportGridStyle);
			table.Font.CopyFrom(ExportGridStyle.Font);
			var exportArgs = new ExportArgs(this, holder, table);
			OnExportHolder(exportArgs);
			var headerArgs = new ExportingHeaderArgs(this, holder, table);
			OnExportHeader(headerArgs);
			if (!headerArgs.Handled) {
				table.Rows.Add(headerArgs.DefaultHeaderRow());
			}


			if (datasource != null) {
				var contentArgs = new ExportingContentArgs(this, holder, table);
				var sourceWrapper = new DataSourceWrapper(datasource);
				bool first = true;
				while (sourceWrapper.MoveNext()) {
					if (first) {
						first = false;
						OnExportBeginContent(exportArgs);
					}
					contentArgs.Handled = false;
					contentArgs.ItemWrapper = sourceWrapper.Current;
					OnExportContent(contentArgs);
					if (!contentArgs.Handled) {
						table.Rows.Add(contentArgs.DefaultContentRow(true));
					}
				}
				if (!first) {
					OnExportEndContent(exportArgs);
				}
			}
			return holder;
		}
		protected virtual void OnExportHolder(ExportArgs e) {
			var handler = (EventHandler<ExportArgs>)Events[ExportHolderEvent];
			if (handler != null) {
				handler(this, e);
			}
		}
		public event EventHandler<ExportArgs> ExportHolder {
			add {
				Events.AddHandler(ExportHolderEvent, value);
			}
			remove {
				Events.RemoveHandler(ExportHolderEvent, value);
			}
		}
		private static readonly object ExportHolderEvent = new object();
		protected virtual void OnExportHeader(ExportingHeaderArgs e) {
			var handler = ExportHeader;
			if (handler != null) {
				handler(this, e);
			}
		}
		protected virtual void OnExportBeginContent(ExportArgs e) {
			var handler = (EventHandler<ExportArgs>)Events[ExportBeginContentEvent];
			if (handler != null) {
				handler(this, e);
			}
		}
		protected virtual void OnExportEndContent(ExportArgs e) {
			var handler = (EventHandler<ExportArgs>)Events[ExportEndContentEvent];
			if (handler != null) {
				handler(this, e);
			}
		}
		/// <summary>
		/// Fire after export header and before  export content 
		/// </summary>
		public event EventHandler<ExportArgs> ExportBeginContent {
			add {
				Events.AddHandler(ExportBeginContentEvent, value);
			}
			remove {
				Events.RemoveHandler(ExportBeginContentEvent, value);
			}
		}

		private static readonly object ExportBeginContentEvent = new object();
		/// <summary>
		/// Fire after finished export content
		/// </summary>
		public event EventHandler<ExportArgs> ExportEndContent {
			add {
				Events.AddHandler(ExportEndContentEvent, value);
			}
			remove {
				Events.RemoveHandler(ExportEndContentEvent, value);
			}
		}
		private static readonly object ExportEndContentEvent = new object();

		public event EventHandler<ExportingHeaderArgs> ExportHeader;
		protected virtual void OnExportContent(ExportingContentArgs e) {
			var handler = (EventHandler<ExportingContentArgs>)Events[ExportContentEvent];
			if (handler != null) {
				handler(this, e);
			}
		}

		private static readonly object GetEditValuesEvent = new object();
		public event EventHandler<GetEditValuesEventArgs> GetEditValues {
			add {
				Events.AddHandler(GetEditValuesEvent, value);
			}
			remove {
				Events.RemoveHandler(GetEditValuesEvent, value);
			}
		}
		protected virtual void OnGetEditValues(GetEditValuesEventArgs e) {
			var results = new string[VisibleColumns.Count * 3];
			var handler = (EventHandler<GetEditValuesEventArgs>)Events[GetEditValuesEvent];
			if (handler != null) {
				handler(this, e);
				{
					if (e.VisibleColumnValues.ColumnValueTextList.Count != VisibleColumns.Count) {
						throw new Exception("Return number of values from GetEditValues event difference from VisibleColumns count");
					}

					for (int i = 0; i < VisibleColumns.Count; i++) {
						var column = VisibleColumns[i];
						bool match = false;
						for (int j = 0; j < e.VisibleColumnValues.ColumnValueTextList.Count; j++) {
							var value = e.VisibleColumnValues.ColumnValueTextList[j];
							if (ReferenceEquals(column, value.Column)) {
								match = true;
								var k = i * 2;
								results[k] = column.InternalGetValue(value.Value, e.DataItem);
								results[k + 1] = value.DisplayText;
								results[k + 2] = value.SetText ? "1" : "0";
								break;
							}
						}
						if (!match) {
							string columnName;
							if (!String.IsNullOrEmpty(column.Caption)) {
								columnName = "Caption: " + column.Caption;
							} else if (!String.IsNullOrEmpty(column.Name)) {
								columnName = "Name: " + column.Name;
							} else if (!String.IsNullOrEmpty(column.FieldName)) {
								columnName = "FieldName: " + column.FieldName;
							} else {
								columnName = "Visible index: " + i;
							}
							throw new Exception("Column " + columnName + " not found in VisibleColumnValues");
						}

						//results[i] = VisibleColumns[i].GetValueFromObject(e.VisibleColumnValues[i]);
					}
				}
			}

			var args = new CustomCallbackEventArgs(String.Empty);
			args.Result = ConvertToArrayString(results);
			ProcessCustomCallbackEventArgs(args);
		}

		private static readonly object ExportContentEvent = new object();
		public event EventHandler<ExportingContentArgs> ExportContent {
			add {
				Events.AddHandler(ExportContentEvent, value);
			}
			remove {
				Events.RemoveHandler(ExportContentEvent, value);
			}
		}

		private static readonly object RowExpendedEvent = new object();
		public event EventHandler<SpRowExpendedEventArgs> RowExpended {
			add {
				Events.AddHandler(RowExpendedEvent, value);
			}
			remove {
				Events.RemoveHandler(RowExpendedEvent, value);
			}
		}

		protected virtual void OnRowExpended(SpRowExpendedEventArgs e) {
			var handler = (EventHandler<SpRowExpendedEventArgs>)Events[RowExpendedEvent];
			if (handler != null) {
				try {
					handler(this, e);
					ProcessCustomCallbackEventArgs(e);
				} catch (Exception ex) {
					WriteError(ex);
				}
			}
		}

		private static readonly object CellCreatedEvent = new object();
		public event EventHandler<SpGvCellCreatedEventArgs> CellCreated {
			add {
				Events.AddHandler(CellCreatedEvent, value);
			}
			remove {
				Events.RemoveHandler(CellCreatedEvent, value);
			}
		}

		private static readonly object RowCreatedEvent = new object();
		public event EventHandler<SpGvRowCreatedEventArgs> RowCreated {
			add {
				Events.AddHandler(RowCreatedEvent, value);
			}
			remove {
				Events.RemoveHandler(RowCreatedEvent, value);
			}
		}

		private static readonly object RowEditedEvent = new object();
		public event EventHandler<SpGvRowEditedEventArgs> RowEdited {
			add {
				Events.AddHandler(RowEditedEvent, value);
			}
			remove {
				Events.RemoveHandler(RowEditedEvent, value);
			}
		}

		private static readonly object BeforeDataBindingEvent = new object();
		public event EventHandler BeforeDataBinding {
			add {
				Events.AddHandler(BeforeDataBindingEvent, value);
			}
			remove {
				Events.RemoveHandler(BeforeDataBindingEvent, value);
			}
		}

		private static readonly object FooterRowCreatingEvent = new object();
		public event EventHandler<SpGvFooterRowCreating> FooterRowCreating {
			add {
				Events.AddHandler(FooterRowCreatingEvent, value);
			}
			remove {
				Events.RemoveHandler(FooterRowCreatingEvent, value);
			}
		}
		protected virtual void OnRowCreated(SpGvRowCreatedEventArgs e) {
			var handler = (EventHandler<SpGvRowCreatedEventArgs>)Events[RowCreatedEvent];
			if (handler != null) handler(this, e);
		}

		protected virtual void OnCellCreated(SpGvCellCreatedEventArgs e) {
			var handler = (EventHandler<SpGvCellCreatedEventArgs>)Events[CellCreatedEvent];
			if (handler != null) handler(this, e);
		}

		protected virtual void OnRowDelete(SpGvRowDeletedEventArgs e) {
			var handler = RowDeleted;
			if (handler != null) {
				try {
					handler(this, e);
					ProcessCustomCallbackEventArgs(e);
				} catch (Exception ex) {
					WriteError(ex);
				}
			}
		}
		private class WrapObject {
			public object Original;
		}
		public event EventHandler<SpGvRowDeletedEventArgs> RowDeleted;

		private class CompateDataItemWrapper : IComparer<DataSourceWrapper.DataItemWraper>, IComparer {
			private readonly SpGvBaseColumn _column;
			private readonly Hashtable _itemHash;
			internal CompateDataItemWrapper(SpGvBaseColumn column) {
				_column = column;
				_itemHash = new Hashtable();
			}

			private object FindValue(DataSourceWrapper.DataItemWraper item) {
				var original = (WrapObject)_itemHash[item.OriginalItem];
				if (original != null) return original.Original;

				object value = item.GetPropertyValue(_column.FieldName);
				switch (_column.ColumnType) {
					case GridColumnType.TextEdit:
					case GridColumnType.SpinEdit:
					case GridColumnType.DateEdit:
						break;
					case GridColumnType.ComboEdit:
						var cbxColumn = (SpGvComboxColumn)_column;
						var wrap = new DataSourceWrapper(cbxColumn.EditCombobox.DataSource);
						var valueWrap = wrap.Find(cbxColumn.EditCombobox.ValueField, value);
						if (!valueWrap.IsEmpty)
							value = valueWrap.GetPropertyValue(cbxColumn.EditCombobox.TextField);
						break;
				}
				_itemHash.Add(item.OriginalItem, new WrapObject { Original = value });
				return value;
			}

			public int Compare(DataSourceWrapper.DataItemWraper x, DataSourceWrapper.DataItemWraper y) {
				object xValue = FindValue(x);
				object yValue = FindValue(y);
				if (xValue == null && yValue == null) {
					return 0;
				}
				if (xValue == null) {
					return -1;
				}

				if (yValue == null) {
					return 1;
				}

				var c = xValue as IComparable;
				if (c != null) {
					return c.CompareTo(yValue);
				}

				return 0;
			}

			public int Compare(object x, object y) {
				return Compare((DataSourceWrapper.DataItemWraper)x, (DataSourceWrapper.DataItemWraper)y);
			}
		}
	}
}
